/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-14 11:30:03
 * @LastEditTime: 2021-09-06 14:56:24
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include "ft_assert.h"
#include "timer_tacho.h"
#include "timer_tacho_hw.h"
// #include "gicv3.h"
#include "interrupt.h"

u32 TimerGetInterruptMask(TimerTachoCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);
    return TIMER_INTR_M_READ(pCtrl);
}

static const u32 g_intrBits[MAX_TIMER_TACHO_EVENT] = 
{
    TACHO_OVER_INTR_EN, TACHO_UNDER_INTR_EN,
    TIMER_ROLLOVER_INTR_EN, TIMER_ONCECMP_INTR_EN, TIMER_CYCCMP_INTR_EN,
    TACHO_CAPTURE_INTR_EN
};
static const u32 g_intrStats[MAX_TIMER_TACHO_EVENT] = 
{
    TACHO_OVER_INTR_STATUS, TACHO_UNDER_INTR_STATUS, 
    TIMER_ROLLOVER_INTR_STATUS, TIMER_ONCECMP_INTR_STATUS, TIMER_CYCCMP_INTR_STATUS, 
    TACHO_CAPTURE_INTR_STATUS
};
static void TimerSetInterruptMask(TimerTachoCtrl *pCtrl, 
                                  TimerTachoEventType intrType,
                                  boolean enable)
{
    FT_ASSERTVOID(pCtrl && intrType < MAX_TIMER_TACHO_EVENT);
    u32 mask = TIMER_INTR_M_READ(pCtrl);

    if (enable)
    {
        mask |= g_intrBits[intrType];
    }
    else
    {
        mask &= ~g_intrBits[intrType];
    }

    TIMER_INTR_M_WRITE(pCtrl, mask);
}

static void TimerTachoIntrHandler(s32 vector, void *param)
{
    FT_ASSERTVOID(param);

    TimerTachoCtrl *pCtrl = (TimerTachoCtrl *)param;
    const u32 IntrStatus = TIMER_INTR_S_READ(pCtrl);
    u32 loop;
    TimerEventHandler evtHandler;

    TIMER_DEBUG_I("intr entered cause: 0x%x", IntrStatus);

    /* check intr status bit by bit */
    for (loop = 0; loop < MAX_TIMER_TACHO_EVENT; loop++)
    {
        evtHandler = pCtrl->EvtHandlers[loop];
        
        if ((g_intrStats[loop] & IntrStatus) && (NULL != evtHandler))
        {
            evtHandler((void *)pCtrl);
        }
    }

    TIMER_INTR_S_CLEAR(pCtrl, IntrStatus);
}

void TimerDefaultEvtCallback(void *pPara)
{
    FT_ASSERTVOID(pPara);
    TimerTachoCtrl *pCtrl = (TimerTachoCtrl *)pPara;
    const u32 IntrStatus = TIMER_INTR_S_READ(pCtrl);
    
    TIMER_DEBUG_I("timer id: 0x%x, intr cause: 0x%x", IntrStatus);
}

void TimerRegisterEvtCallback(TimerTachoCtrl *pCtrl, 
                              TimerTachoEventType EvtType, 
                              TimerEventHandler Callback)
{
    FT_ASSERTVOID(pCtrl && EvtType < MAX_TIMER_TACHO_EVENT);
    pCtrl->EvtHandlers[EvtType] = Callback;
}

void TimerDisableIntr(TimerTachoCtrl *pCtrl)
{
    u32 irqID = TIMER_TACHO_IRQ_ID(pCtrl->Config.Id);
    
    InterruptMask(irqID);
}

u32 TimerEnableIntr(TimerTachoCtrl *pCtrl)
{
    FT_ASSERTZERONUM(pCtrl);

    u32 irqID = TIMER_TACHO_IRQ_ID(pCtrl->Config.Id);

    /* disable all timer irq */
    TIMER_INTR_M_WRITE(pCtrl, 0x0);

    /* umask timer irq */
    InterruptSetPriority(irqID, pCtrl->Config.IrqPriority);
    InterruptInstall(irqID, TimerTachoIntrHandler, pCtrl, pCtrl->Config.Name); 

    /* enable irq */
    InterruptUmask(irqID);

    if (TIMER_WORK_MODE_TIMER == pCtrl->Config.WorkMode)
    {
        if (TIMER_ONCE_CMP == pCtrl->Config.CmpType)
        {
            TimerSetInterruptMask(pCtrl, TIMER_EVENT_ONCE_CMP, TRUE); 
            TimerRegisterEvtCallback(pCtrl, TIMER_EVENT_ONCE_CMP, TimerDefaultEvtCallback);
        }
        else
        {
            TimerSetInterruptMask(pCtrl, TIMER_EVENT_CYC_CMP, TRUE);
            TimerRegisterEvtCallback(pCtrl, TIMER_EVENT_CYC_CMP, TimerDefaultEvtCallback);
        }
        
        TimerSetInterruptMask(pCtrl, TIMER_EVENT_ROLL_OVER, TRUE);        
        TimerRegisterEvtCallback(pCtrl, TIMER_EVENT_ROLL_OVER, TimerDefaultEvtCallback);
    }
    else if (TIMER_WORK_MODE_TACHO == pCtrl->Config.WorkMode)
    {
        TimerSetInterruptMask(pCtrl, TACHO_EVENT_OVER, TRUE);
        TimerSetInterruptMask(pCtrl, TACHO_EVENT_UNDER, TRUE);
        TimerRegisterEvtCallback(pCtrl, TACHO_EVENT_OVER, TimerDefaultEvtCallback);
        TimerRegisterEvtCallback(pCtrl, TACHO_EVENT_UNDER, TimerDefaultEvtCallback);
    }
    else if (TIMER_WORK_MODE_CAPTURE == pCtrl->Config.WorkMode)
    {
        TimerSetInterruptMask(pCtrl, TACHO_EVENT_CAPTURE, TRUE);
        TimerRegisterEvtCallback(pCtrl, TACHO_EVENT_CAPTURE, TimerDefaultEvtCallback);
    }
    else
    {
        FT_ASSERTZERONUM(0);
    }

    return TIMER_SUCCESS;
}